/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/07/18
* File: RawSceneNode.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include "../RenderSystem/MaterialData.h"
#include "../RenderSystem/Texture.h"
#include "../RenderSystem/RenderSystem.h"
#include "CameraSceneNode.h"
#include "../Model/cube.h"
#include "SceneManager.h"
#include "../RenderSystem/GraphicsBuffer.h"
#include "CameraSceneNode.h"
#include "RawSceneNode.h"

RawSceneNode::RawSceneNode(SceneNode* parent, 
						   SceneManager* scene_mgr, 
						   s32 id /* = -1 */, 
						   s32 type /* = 0 */, 
						   const glm::vec3& position,
						   const glm::vec3& rotation,
						   const glm::vec3& scale)
						   : SceneNode(parent, scene_mgr, id, type, position, rotation, scale)
						   , _vbo_buffer(NULL)
						   , _idx_buffer(NULL)
						   , _effect_service(NULL)
{

}

RawSceneNode::~RawSceneNode()
{
	safe_delete(_vbo_buffer);
	safe_delete(_idx_buffer);
}

void RawSceneNode::render()
{
	if (!_vbo_buffer || !_idx_buffer) return;

	RenderSystem* render_system = _scene_mgr->getRenderSystem();
	const glm::mat4& world = getAbsoluteTransformation();

	render_system->setGraphicsBuffer(BS_VERTEX_BUFFER, _vbo_buffer);
	render_system->setGraphicsBuffer(BS_INDEX_BUFFER, _idx_buffer);

	render_system->setTransform(TS_World, world);
	render_system->setMaterial(&_material);
	render_system->setFVF(_fvf_state);
	render_system->setEffectService(_effect_service);
	render_system->drawPrimitiveIndex(_primitive_count, PT_TRIANGLES, 0, _is_index_16bit);
}

void RawSceneNode::setVertexBuffer(void* data, u32 size_in_byte, FVFState state)
{
	_fvf_state = state;

	RenderSystem* render_system = _scene_mgr->getRenderSystem();

	_vbo_buffer = render_system->createGraphicsBuffer(BU_Static, BA_Read_Write, BS_VERTEX_BUFFER);

	_vbo_buffer->resize(size_in_byte);
	GraphicsBuffer::Mapper vbo_mapper(*_vbo_buffer, BA_Write_Only);

	u8* vbo_raw_buffer = vbo_mapper.Pointer<u8>();
	memcpy(vbo_raw_buffer, data, _vbo_buffer->size());
}

void RawSceneNode::setIndexBuffer(void* data, u32 primitive_count, bool is_index_16bit)
{
	_primitive_count = primitive_count;
	_is_index_16bit = is_index_16bit;

	RenderSystem* render_system = _scene_mgr->getRenderSystem();
	_idx_buffer = render_system->createGraphicsBuffer(BU_Static, BA_Read_Write, BS_INDEX_BUFFER);

	u32 size = _primitive_count * (_is_index_16bit ? sizeof(u16) : sizeof(u32));

	_idx_buffer->resize(size);
	GraphicsBuffer::Mapper idx_mapper(*_idx_buffer, BA_Write_Only);

	u8* idx_raw_buffer = idx_mapper.Pointer<u8>();
	memcpy(idx_raw_buffer, data, _idx_buffer->size());
}

void RawSceneNode::setMaterial(const MaterialData& material)
{
	_material = material;
}

void RawSceneNode::setRenderEffectService(EffectService* es)
{
	_effect_service = es;
}

void RawSceneNode::move(f32 distance)
{
	if (iszero(distance)) return;

	CameraSceneNode* camera = _scene_mgr->getActiveCameraNode();

	glm::vec3 eye_pos = camera->getEyePos();
	glm::vec3 target_pos = camera->getTargetPos();

	glm::vec3 look = target_pos - eye_pos;

	look.y = 0;

	glm::vec3 v_dis = look * distance;

	_relative_translation += v_dis;
}

void RawSceneNode::moveLR(f32 distance)
{
	if (iszero(distance)) return;

	CameraSceneNode* camera = _scene_mgr->getActiveCameraNode();

	glm::vec3 eye_pos = camera->getEyePos();
	glm::vec3 target_pos = camera->getTargetPos();

	glm::vec3 look = target_pos - eye_pos;

	glm::vec3 v_dis = look * distance;

	v_dis = glm::vec3(v_dis.z, 0, -v_dis.x);

	_relative_translation += v_dis;
}